//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:

Littleton_serial.cpp

Abstract:
        Littleton BSP Serial Driver.

Notes:

--*/

#include <windows.h>
#include <types.h>
#include <ceddk.h>

#include <ddkreg.h>
#include <serhw.h>
#include <hw16550.h>
#include <Serdbg.h>

#include <monahans.h>
#include <xllp_gpio.h>
#include <16550.h>

#include <Littleton.h>
#include <xllp_bcr.h>

#include <notify.h>

#include <args.h>
#include <ioctl_cfg.h>
#include <string.h>

#include <xllp_i2c.h>
#include <xllp_gpio.h>
#include "xllp_gpio_plat.h"
#include <xllp_uart_plat.h>
#include <xllp_irda_plat.h>
#include <xllp_pm.h>
#include "GPX_XLLP.h"

#define MBW_SLAVEADDR 0x76 //slaveaddr of mbw - gpio expander
#define INPORT0  0
#define INPORT1  1
#define OUTPORT0 2
#define OUTPORT1 3
#define CFGPORT0 6
#define CFGPORT1 7

//---------------------------------Individual UART PORT -----------------------------------
//---------------------------------FUART PORT -----------------------------------
class CPXAPdd16550FUART : public CPXAPdd16550 {
public:

    CPXAPdd16550FUART(LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj):CPXAPdd16550(lpActivePath,pMdd, pHwObj)
    {
        wcscpy(szName, L"FFUART");
        m_bRILFFUARTFlag = FALSE;
    };

    ~CPXAPdd16550FUART()
    {
        m_bRILFFUARTFlag = FALSE;
    }

    virtual BOOL CPXAPdd16550FUART::Init()
    {
        BSP_ARGS args;
        DWORD size;
        HKEY hKey=NULL;
        TCHAR s1[100];

        if(KernelIoControl(IOCTL_GET_BSP_ARGS,NULL,0,&args,sizeof(args),&size))
        {
            if(args.FFUARTUsage&BSP_ARGS_FFUART_DEBUG_SERIAL)
            {
                RETAILMSG(1, (_T("[FFUART]EBOOT Config FFUART for debug serial, it can not be used for other application!\r\n")));
                return FALSE;
            }

        // ActiveSync support routines, removed since Littleton mh-lv doesn't have enough pins
            // if(args.FFUARTUsage&BSP_ARGS_FFUART_ACTIVESYNC)
            // {
                 // wcscpy(s1,L"{CC5195AC-BA49-48a0-BE17-DF6D1B0173DD}");

                 // if(ERROR_SUCCESS ==RegSetValueEx(PC_REG_SERIALFICLASS_VAL_NAME, REG_SZ,(PBYTE)s1,wcslen(s1)*2))
                     // RETAILMSG(UART_DEBUG, (_T("[FFUART]set the IClass for a non power manageable interface\r\n")));

                 // if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, PC_REG_SERIALFRIENDLYNAME_PATH,0,0, &hKey))
                 // {
                    // wcscpy(s1,L"Serial Cable on COM1:");

                    // if(ERROR_SUCCESS == ::RegSetValueEx(hKey,PC_REG_SERIALFRIENDLYNAME_VAL_NAME,NULL, REG_SZ,(PBYTE)s1,wcslen(s1)*2))
                        // RETAILMSG(UART_DEBUG, (_T("[FFUART]set the FriendlyName as Serial Cable on COM1:\r\n")));
                    // else
                        // RETAILMSG(UART_DEBUG, (_T("[FFUART]fail to set the FriendlyName as Serial Cable on COM1:\r\n")));

                    // RETAILMSG(1, (_T("[FFUART]EBOOT Config FFUART for ActiveSync or other applications!\r\n")));
                    // RegCloseKey(hKey);
                 // }

                  // /* Hardware flow control had been enable when ActiveSync used FFUART */
                  // m_AutoFlowControlEnabled = 2;
                  // m_DMAEnabled = 1;
             // }


             if(args.FFUARTUsage&BSP_ARGS_FFUART_RIL)
             {

                 wcscpy(s1,L"{A32942B7-920C-486b-B0E6-92A702A99B35}");
                 if(ERROR_SUCCESS ==RegSetValueEx(PC_REG_SERIALFICLASS_VAL_NAME, REG_SZ,(PBYTE)s1,wcslen(s1)*2))
                     RETAILMSG(UART_DEBUG, (_T("[FFUART]set the IClass for a power manageable interface\r\n")));

                 if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, PC_REG_SERIALFRIENDLYNAME_PATH,0,0, &hKey))
                 {
                    wcscpy(s1,L"COM1:");
                    if(ERROR_SUCCESS == ::RegSetValueEx(hKey,PC_REG_SERIALFRIENDLYNAME_VAL_NAME, NULL,REG_SZ,(PBYTE)s1,wcslen(s1)*2))
                         RETAILMSG(UART_DEBUG, (_T("[FFUART]set the FriendlyName COM1:\r\n")));
                    else
                        RETAILMSG(UART_DEBUG, (_T("[FFUART]fail to set the FriendlyName COM1:\r\n")));

                    RETAILMSG(1, (_T("[FFUART]EBOOT Config FFUART for RIL!\r\n")));
                    RegCloseKey(hKey);
                 }

                  /* FFUART is used by RIL */
                  m_bRILFFUARTFlag = TRUE;

                  /* Hardware flow control had been enable when RIL used FFUART */
                  m_AutoFlowControlEnabled = 2;
                  m_DMAEnabled = 1;
            }
        }

        m_UART_Handle.UARTSelection = XLLP_FFUART;

        if (!CPXAPdd16550::Init())
        {
            RETAILMSG(1, (_T("[FFUART] Failed in CPXAPdd16550::Init()\r\n")));
            return FALSE;
        }

        return ConfigurePinout();
    }

    virtual void CPXAPdd16550FUART::PostInit()
    {
        CPXAPdd16550::PostInit();

        //Enable interrupts and UART to take care of rx data without COM_Open()
        //so that event "NOTIFICATION_EVENT_RS232_DETECTED" get chance to be notified

        RETAILMSG(UART_DEBUG, (TEXT("Enable interrupts and UART to take care of rx data without COM_Open().\r\n")));
        m_HardwareLock.Lock();
        BYTE bIER = GetRegister()->Read_IER();
        GetRegister()->Write_IER(bIER | PXA_SERIAL_IER_UUE);
        m_HardwareLock.Unlock();

    }

    virtual ULONG   CPXAPdd16550FUART::GetModemStatus()
    {
        m_HardwareLock.Lock();
        ULONG ulReturn =0 ;
        ULONG Events = 0;
        UINT8 ubModemStatus = GetRegister()->Read_MSR();
        m_HardwareLock.Unlock();

        // Event Notification.
        if (ubModemStatus & SERIAL_MSR_DCTS)
        {
            Events |= EV_CTS;
            RETAILMSG(UART_DEBUG, (TEXT("#### EV_CTS changed####\r\n")));
        }
        if ( ubModemStatus  & SERIAL_MSR_DDSR )
        {
            Events |= EV_DSR;
            RETAILMSG(UART_DEBUG, (TEXT("#### EV_DSR changed####\r\n")));
        }
        if ( ubModemStatus  & SERIAL_MSR_TERI )
        {
            Events |= EV_RING;
            RETAILMSG(UART_DEBUG, (TEXT("#### EV_RING changed####\r\n")));
        }
        if ( ubModemStatus  & SERIAL_MSR_DDCD )
        {
            Events |= EV_RLSD;
            RETAILMSG(UART_DEBUG, (TEXT("#### EV_RLSD changed####\r\n")));
        }
        // Report Modem Status;
        if ( ubModemStatus & SERIAL_MSR_CTS )
        {
            ulReturn |= MS_CTS_ON;
            RETAILMSG(UART_DEBUG, (TEXT("#### MS_CTS_ON ####\r\n")));
        }
        if ( ubModemStatus & SERIAL_MSR_DSR )
        {
            ulReturn |= MS_DSR_ON;
            RETAILMSG(UART_DEBUG, (TEXT("#### MS_DSR_ON ####\r\n")));
        }
        if ( ubModemStatus & SERIAL_MSR_RI )
        {
            ulReturn |= MS_RING_ON;
            RETAILMSG(UART_DEBUG, (TEXT("#### MS_RING_ON ####\r\n")));
        }
        if ( ubModemStatus & SERIAL_MSR_DCD )
        {
            ulReturn |= MS_RLSD_ON;
            RETAILMSG(UART_DEBUG, (TEXT("#### MS_RLSD_ON ####\r\n")));
        }

        if (Events!=0)
            EventCallback(Events,ulReturn);

        /* Check if it is use software flow control */
        if (2 != m_AutoFlowControlEnabled)
        {
            if (m_DMAEnabled)
            {
                /* For DMA Enabled, it need to stop DMA channel for flow control when needed */
                if ((m_DCB.fOutxCtsFlow && ((ulReturn & MS_CTS_ON) == 0))
                || (m_DCB.fOutxDsrFlow && ((ulReturn & MS_DSR_ON) == 0)))
                {
                    // We are in flow off
                    RETAILMSG(1, (TEXT("[FFUART]GetModemStatus(): Flow Off, Data Discard.\r\n")));
                    //  Stop the Transfer to cause timeout
                    m_DMAC_HardwareLock.Lock();
                    XllpDmacStopTransfer(m_DMAC_TX_Handle, m_DMATxChannel);
                    m_DMAC_HardwareLock.Unlock();
                }
            }
            else
            {
                /* For DMA Not Enabled, it need to disable interrupt when needed */
                if (m_DCB.fOutxCtsFlow && (Events & EV_CTS))
                {
                    if (ulReturn & MS_CTS_ON)
                        EnableXmitInterrupt(TRUE);
                    else
                        EnableXmitInterrupt(FALSE);
                }
            }
        }
        else
        {
            /* It is hardware flow control, it needn't do nothing */
            RETAILMSG(UART_DEBUG, (TEXT("[FFUART]: CPXAPdd16550::GetModemStatus hardware flow handling\r\n")));
        }

        #if AUTODETECT_USING_DSR
        // DCD signal is incorrect on some boards: is always LOW and never changes
        // This works around is to detect DSR signal instead
        // as DSR & CD are jumpered for serial null-modem cable (DB9-DB9).
        if (!IsOpen() && ((Events & EV_DSR)!=0 && (ulReturn & MS_DSR_ON)!=0))
        {
            RETAILMSG(UART_DEBUG, (TEXT("[UART]NOTIFICATION_EVENT_RS232_DETECTED\r\n")));
            CeEventHasOccurred (NOTIFICATION_EVENT_RS232_DETECTED, NULL);
        }
        #endif // end #if AUTODETECT_USING_DSR

        return ulReturn;
    }

    virtual BOOL  CPXAPdd16550FUART::Open()
    {
        BOOL bReturn;
        XllpClockEnable(m_pDCCLKReg, XLLP_CLK_FFUART, XLLP_TRUE);

        bReturn = CPXAPdd16550::Open();
        return bReturn;
    }
    virtual BOOL  CPXAPdd16550FUART::Close()
    {
        BOOL bReturn =  CPXAPdd16550::Close();
        if (bReturn)
        {
            //Enable interrupts and UART to take care of rx data without COM_Open()
            //so that event "NOTIFICATION_EVENT_RS232_DETECTED" get chance to be notified
            m_HardwareLock.Lock();
            GetRegister()->Write_IER(GetRegister()->Read_IER() |PXA_SERIAL_IER_UUE);
            m_HardwareLock.Unlock();
        }

        //Disable FFUART clock
        XllpClockEnable(m_pDCCLKReg, XLLP_CLK_FFUART, XLLP_FALSE);

        return bReturn;
    }

    virtual void  CPXAPdd16550FUART::SerialRegisterBackup()
    {
        #ifdef PRODUCT_SERIAL_ENABLED
         //NKDbgPrintfW(TEXT("Littleton_SERIAL***: Calling CPXAPdd16550::SerialRegisterBackup\r\n"));
         CPXAPdd16550::SerialRegisterBackup();

         //NKDbgPrintfW(TEXT("LITTLETON_SERIAL***: Disabling UART\r\n"));
         BYTE bIER = GetRegister()->Read_IER();
         GetRegister()->Write_IER(bIER & ~PXA_SERIAL_IER_UUE);

         //Disable FFUART clock
         //NKDbgPrintfW(TEXT("LITTLETON_SERIAL***: Disabling FFUART clock\r\n"));
         XllpClockEnable(m_pDCCLKReg, XLLP_CLK_FFUART, XLLP_FALSE);
         #endif
     }

    virtual void  CPXAPdd16550FUART::SerialRegisterRestore()
    {
        #ifdef PRODUCT_SERIAL_ENABLED
        //NKDbgPrintfW(TEXT("LITTLETON_SERIAL***: Configuring FFUART pins\r\n"));
        ConfigurePinout();

        //Enable FFUART clock
        //NKDbgPrintfW(TEXT("LITTLETON_SERIAL***: Enabling FFUART clock\r\n"));
        XllpClockEnable(m_pDCCLKReg, XLLP_CLK_FFUART, XLLP_TRUE);

        //Restoring rest of registers
        //NKDbgPrintfW(TEXT("LITTLETON_SERIAL***: Calling CPXAPdd16550::SerialRegisterRestore\r\n"));
        CPXAPdd16550::SerialRegisterRestore();

        //AG change
        //NKDbgPrintfW(TEXT("LITTLETON_SERIAL***: Enabling UART\r\n"));
        BYTE bIER = GetRegister()->Read_IER();
        GetRegister()->Write_IER(bIER | PXA_SERIAL_IER_UUE);
        #else
        ConfigurePinout();
        CPXAPdd16550::SerialRegisterRestore();
        #endif

        if (m_DMAEnabled)
        {
            /* It need to re configure DMA receive channel register after wake up from sleep */
            CPXAPdd16550::DMAReInitRXChannel();
        }

    }

    virtual BOOL CPXAPdd16550FUART::Ioctl(DWORD dwCode,PBYTE pBufIn,DWORD dwLenIn,PBYTE pBufOut,DWORD dwLenOut,PDWORD pdwActualOut)
    {
        BOOL RetVal=FALSE;

        switch ( dwCode )
        {

            case IOCTL_POWER_CAPABILITIES:
            {
                if (!pBufOut || dwLenOut < sizeof(POWER_CAPABILITIES) || !pdwActualOut)
                {
                    SetLastError(ERROR_INVALID_PARAMETER);
                    RetVal = FALSE;
                }
                else
                {
                    m_IsThisPowerManaged= TRUE;
                    __try
                    {
                        PPOWER_CAPABILITIES ppc = (PPOWER_CAPABILITIES)pBufOut;
                        *ppc= GetPowerCapabilities();
                        if (pdwActualOut)
                            *pdwActualOut = sizeof(POWER_CAPABILITIES);
                        RetVal = TRUE;
                    }
                    __except(EXCEPTION_EXECUTE_HANDLER)
                    {
                        RETAILMSG( UART_DEBUG, ( _T("[FFUART]IOCTL_POWER_CAPABILITIES Exception!") ) );
                        SetLastError(ERROR_INVALID_PARAMETER);
                        RetVal = FALSE;
                    }
                }
                break;
            }

            case IOCTL_POWER_SET:
            {
                if (!pBufOut || dwLenOut < sizeof(CEDEVICE_POWER_STATE) || !pdwActualOut || m_PowerHelperHandle == INVALID_HANDLE_VALUE )
                {
                    SetLastError(ERROR_INVALID_PARAMETER);
                    RetVal = FALSE;
                }
                else
                {
                    m_IsThisPowerManaged= TRUE;

                    __try
                    {

                        CEDEVICE_POWER_STATE newDx = *(PCEDEVICE_POWER_STATE) pBufOut;
                        DEBUGMSG(1, (TEXT("COM: IOCTL_POWER_SET: D%d\r\n"), newDx));

                        /* Enable FF_RI wake up source for incoming call only when FFUART is used by RIL */
                        if ((newDx == D3) || (newDx == D4))
                        {
                            if (m_UART_Handle.pMfpRegBase && m_bRILFFUARTFlag)
                            {
                                XLLP_STATUS_T enStatus = XLLP_STATUS_SUCCESS;

                                /* Current, it can only use disable and enable wake up register to reset MFP register for FF_RI */
                                enStatus = XllpFFUARTEnableLpmWakeup(m_UART_Handle.pMfpRegBase, XLLP_OFF);

                                if (XLLP_STATUS_SUCCESS != enStatus)
                                {
                                    NKDbgPrintfW(TEXT("[FFUART]::Ioctl:IOCTL_POWER_SET: XllpFFUARTEnableLpmWakeup Failed(XLLP_OFF)\r\n"));
                                }

                                enStatus = XllpFFUARTEnableLpmWakeup(m_UART_Handle.pMfpRegBase, XLLP_ON);

                                if (XLLP_STATUS_SUCCESS != enStatus)
                                {
                                    NKDbgPrintfW(TEXT("[FFUART]::Ioctl:IOCTL_POWER_SET: XllpFFUARTEnableLpmWakeup Failed(XLLP_ON)\r\n"));
                                }
                            }
                        }

                        RetVal = DDKPwr_SetDeviceLevel( m_PowerHelperHandle, newDx, NULL );

                        // did we set the device power
                        if(RetVal == TRUE)
                        {
                          //set to an adjusted power state if the driver does not support the requested power state.
                              CEDEVICE_POWER_STATE pwrGet = DDKPwr_GetDeviceLevel(m_PowerHelperHandle );
                              RETAILMSG(UART_DEBUG, (TEXT("[FFUART]: DDKPwr_GetDeviceLevel=D%d\r\n"), pwrGet));
                              *(PCEDEVICE_POWER_STATE) pBufOut = pwrGet;
                              *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
                        }
                        else
                              SetLastError(ERROR_INVALID_PARAMETER);

                        switch(newDx)
                        {
                            case D0:
                            case D1:
                            case D2:
                                XllpClockEnable(m_pDCCLKReg, XLLP_CLK_FFUART, XLLP_TRUE);
                                break;
                            case D3:
                                XllpClockEnable(m_pDCCLKReg, XLLP_CLK_FFUART, XLLP_FALSE);
                                break;
                            default:
                                break;
                        }

                        if (newDx == 0)
                        {
                            //NKDbgPrintfW(TEXT("MS2_Serial***: Forcibly Calling SerialRegisterRestore\r\n"));
                            SerialRegisterRestore();
                        }
                    }
                    __except(EXCEPTION_EXECUTE_HANDLER)
                    {
                        RETAILMSG( UART_DEBUG, ( _T("[FFUART]IOCTL_POWER_SET Exception!") ) );
                        SetLastError(ERROR_INVALID_PARAMETER);
                        RetVal=FALSE;
                    }
                }

                break;
            }

            case IOCTL_POWER_GET:
            {
                if (!pBufOut || dwLenOut < sizeof(CEDEVICE_POWER_STATE) || !pdwActualOut) {
                    SetLastError(ERROR_INVALID_PARAMETER);
                    RetVal = FALSE;
                }
                else
                {
                    __try
                    {
                        *(PCEDEVICE_POWER_STATE) pBufOut =GetDevicePowerState();
                        *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
                        RetVal = TRUE;
                    }
                    __except(EXCEPTION_EXECUTE_HANDLER)
                    {
                        RETAILMSG( UART_DEBUG, ( _T("[FFUART]IOCTL_POWER_SET Exception!") ) );
                        SetLastError(ERROR_INVALID_PARAMETER);
                        RetVal=FALSE;
                    }
                }

                break;
            }

            case IOCTL_REGISTER_POWER_RELATIONSHIP:
                //This IOCTL notifies the parent device so the parent device can register all devices it controls.
                RetVal = TRUE;
                break;

            default:
                SetLastError(ERROR_INVALID_PARAMETER);
                RetVal = FALSE;
                break;
        }

        return RetVal;
    }

private:

    BOOL  m_bRILFFUARTFlag;

    BOOL ConfigurePinout()
    {
        XLLP_STATUS_T returnValue = XLLP_STATUS_FAILURE;

        RETAILMSG(1, (TEXT("[UART] FFUART ConfigurePinout().\r\n")));


        //Initialize GPIO pins
        m_UART_Handle.UARTSelection = XLLP_FFUART;
        m_UART_Handle.pUARTRegs = (P_XLLP_UART_T) m_pBaseAddress;
        m_UART_Handle.pMfpRegBase = (XLLP_VUINT32_T *) m_pMFPReg ;
        m_UART_Handle.pGPIORegs = m_pGPIOReg;
        m_UART_Handle.pMfpRmDb = m_pMfpRMDb;
        m_UART_Handle.MfpRmDBID = XLLP_MFP_RM_ID_XLLP_FFUART;
        m_UART_Handle.Mask = XLLP_DLL_DLH_SET | XLLP_LCR_SET | XLLP_FCR_SET
            | XLLP_FCR_SET | XLLP_IER_SET | XLLP_ABR_SET
            | XLLP_MCR_SET | XLLP_ISR_SET;

        //Configure FFUART for 115200 by default for cellular modem
        m_UART_Handle.DLL_Value = XLLP_DLL_115200BAUD;
        m_UART_Handle.DLH_Value = XLLP_DLH_115200BAUD;
        m_UART_Handle.LCR_Value= 0;
        m_UART_Handle.FCR_Value= 0;
        m_UART_Handle.IER_Value= 0;
        m_UART_Handle.ABR_Value= 0;
        m_UART_Handle.MCR_Value= 0;
        m_UART_Handle.ISR_Value= 0;

        /* Configure FFUART for RIL if m_bRILFFUARTFlag Is True */
        if (m_bRILFFUARTFlag)
        {

            /* Use 57600 baud rate for base band */
            m_UART_Handle.DLL_Value = XLLP_DLL_57600BAUD;
            m_UART_Handle.DLH_Value = XLLP_DLH_57600BAUD;

            /* FF_TXD, FF_RTS, FF_DTR as output, and reset */
            returnValue = XllpGpioSetDirection(m_UART_Handle.pGPIORegs,
                                               XLLP_GPIO_FF_TXD,
                                               XLLP_GPIO_DIRECTION_OUT);

            if (XLLP_STATUS_SUCCESS != returnValue)
            {
                RETAILMSG(1, (TEXT("[UART] FFUART Failed in XllpGpioSetDirection(TXD), Error = %d .\r\n"), returnValue));
                return FALSE;
            }

            returnValue = XllpGpioSetDirection(m_UART_Handle.pGPIORegs,
                                              XLLP_GPIO_FF_RTS,
                                              XLLP_GPIO_DIRECTION_OUT);

            if (XLLP_STATUS_SUCCESS != returnValue)
            {
                RETAILMSG(1, (TEXT("[UART] FFUART Failed in XllpGpioSetDirection(RTS), Error = %d .\r\n"), returnValue));
                return FALSE;
            }


           /* XllpGpioSetDirection(m_UART_Handle.pGPIORegs,
                               XLLP_GPIO_FF_DTR,
                               XLLP_GPIO_DIRECTION_OUT);
            returnValue = XllpGpioSetOutputLevel(m_UART_Handle.pGPIORegs, XLLP_GPIO_FF_TXD, XLLP_HI);

            if (XLLP_STATUS_SUCCESS != returnValue)
            {
                RETAILMSG(1, (TEXT("[UART] FFUART Failed in XllpGpioSetOutputLevel(TXD), Error = %d .\r\n"), returnValue));
                return FALSE;
            }*/


            returnValue = XllpGpioSetOutputLevel(m_UART_Handle.pGPIORegs, XLLP_GPIO_FF_RTS, XLLP_HI);
            if (XLLP_STATUS_SUCCESS != returnValue)
            {
                RETAILMSG(1, (TEXT("[UART] FFUART Failed in XllpGpioSetOutputLevel(RTS), Error = %d .\r\n"), returnValue));
                return FALSE;
            }

           // XllpGpioSetOutputLevel(m_UART_Handle.pGPIORegs, XLLP_GPIO_FF_DTR, XLLP_HI);

            /* FF_RXD, FF_DCD, FF_DSR, FF_RI, FF_CTS as input */
            returnValue = XllpGpioSetDirection(m_UART_Handle.pGPIORegs,
                               XLLP_GPIO_FF_RXD,
                               XLLP_GPIO_DIRECTION_IN);
            if (XLLP_STATUS_SUCCESS != returnValue)
            {
                RETAILMSG(1, (TEXT("[UART] FFUART Failed in XllpGpioSetDirection(RXD), Error = %d .\r\n"), returnValue));
                return FALSE;
            }

          /*  returnValue = XllpGpioSetDirection(m_UART_Handle.pGPIORegs,
                               XLLP_GPIO_FF_RI,
                               XLLP_GPIO_DIRECTION_IN);

            if (XLLP_STATUS_SUCCESS != returnValue)
            {
                RETAILMSG(1, (TEXT("[UART] FFUART Failed in XllpGpioSetDirection(RI), Error = %d .\r\n"), returnValue));
                return FALSE;
            }*/

            returnValue = XllpGpioSetDirection(m_UART_Handle.pGPIORegs,
                               XLLP_GPIO_FF_CTS,
                               XLLP_GPIO_DIRECTION_IN);

            if (XLLP_STATUS_SUCCESS != returnValue)
            {
                RETAILMSG(1, (TEXT("[UART] FFUART Failed in XllpGpioSetDirection(CTS), Error = %d .\r\n"), returnValue));
                return FALSE;
            }

            // Set the wakeup bit in the AD2D0ER register
            PHYSICAL_ADDRESS PA;
            PA.QuadPart  = MONAHANS_BASE_REG_PA_BPMU;
            volatile XLLP_PM_BPMU_T *pBPMU =  (XLLP_PM_BPMU_T *) MmMapIoSpace(PA, sizeof(XLLP_PM_BPMU_T), FALSE);

            pBPMU->ad2d0er |= 0x20;

            RETAILMSG(UART_DEBUG, (TEXT("[UART] FFUART configuration for RIL.\r\n")));
        }

        returnValue = XllpUartInit(&m_UART_Handle);
        if (XLLP_STATUS_SUCCESS != returnValue )
        {
            NKDbgPrintfW(TEXT("\r\n[UART] Fail to XllpUartInit(FFUART).\r\n"));
            RETAILMSG(1,(TEXT("[UART] Fail to XllpUartInit(FFUART): Code#%x.\r\n"), returnValue));
            return FALSE;
        }
        return TRUE;
    }
};

//---------------------------------BUART PORT -----------------------------------
class CPXAPdd16550BUART : public CPXAPdd16550
{
public:
    CPXAPdd16550BUART (LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj)
        : CPXAPdd16550 (lpActivePath,pMdd, pHwObj)
    {
        wcscpy(szName, L"BTUART");

        m_pGPIOReg = NULL;

    };
    ~CPXAPdd16550BUART()
    {
        /* Check if it also need to disable Bluetooth Combo card clock */
        if (m_bBluetoothUsageFlag)
        {
            /* Currrently, only support Rev 1.2 combo card */
            // Disable the BT 26MHz clock
            MBW_BTClkEnable(FALSE);
        }

        if (m_pGPIOReg)
        {
            MmUnmapIoSpace((PVOID)m_pGPIOReg, sizeof(XLLP_GPIO_T));
            m_pGPIOReg = NULL;
        }

        m_bBluetoothUsageFlag = FALSE;
    }

    virtual BOOL CPXAPdd16550BUART::Init()
    {
        BSP_ARGS args;
        DWORD size;

        /* Default, Bluetooth is disabled */
        m_bBluetoothUsageFlag = FALSE;
        m_bBTOpenFlag = FALSE;

        /* Check EBOOT BSP Configuration if it need to turn Bluetooth Module */
        if(KernelIoControl(IOCTL_GET_BSP_ARGS,NULL,0, &args, sizeof(args), &size))
        {
            NKDbgPrintfW(TEXT("[BTUART: Bluetooth] ConfigurePinout args.TechBoardUsage = 0x%x!\r\n"), args.TechBoardUsage);

            if ((args.TechBoardUsage == BSP_ARGS_TECHBOARD_BT) || (args.TechBoardUsage == BSP_ARGS_TECHBOARD_BT_WLAN))
            {
                RETAILMSG(1, (_T("[BTUART: Bluetooth] Bluetooth will be enalbed +!\r\n")));

                m_bBluetoothUsageFlag = TRUE;

                // Map the I2C and OST register address
               // PHYSICAL_ADDRESS PA;

                //PA.QuadPart  = MONAHANS_BASE_REG_PA_GPIO;
              //  m_pGPIORegs  = (P_XLLP_GPIO_T) MmMapIoSpace(PA, sizeof(XLLP_GPIO_T), FALSE);


            }
            else
            {
                RETAILMSG(1, (_T("[BTUART: Bluetooth] Bluetooth will not be enalbed.\r\n")));

                /* Currently, the BTUART will not be loaded if the Bluetooth is not required */
                RETAILMSG(1, (_T("[BTUART] BTUART will not be enalbed.\r\n")));
                m_bBluetoothUsageFlag = FALSE;
                return FALSE;
            }
        }

        m_UART_Handle.UARTSelection = XLLP_BTUART;

        /* For Bluetooth usage, it need to set DMA mode and hardware flow control */
        m_AutoFlowControlEnabled = 2;
        m_DMAEnabled = 1;

        if (CPXAPdd16550::Init())
        {
            ConfigurePinout();
            RETAILMSG(1, (_T("[BTUART] Init Successful!\r\n")));
        }
        else
        {
            RETAILMSG(1, (_T("[BTUART] Init Failed!\r\n")));
            return FALSE;
        }

        return TRUE;
    }

    virtual BOOL  CPXAPdd16550BUART::Open()
    {
        BOOL bReturn;

        // Enable BTUART clock during driver is opened
        XllpClockEnable(m_pDCCLKReg, XLLP_CLK_BTUART, XLLP_TRUE);


        bReturn = CPXAPdd16550::Open();

        /* Check if it need to turn on Bluetooth part on Combo card */
        if (m_bBluetoothUsageFlag)
        {
            RETAILMSG(1, (_T("[BTUART: Bluetooth] Start Enable BT.\r\n")));

            /* Currrently, only support Rev 1.2 combo card */
            // Enable the BT 26MHz clock
            MBW_BTClkEnable(TRUE);
            // Make the BT core out of the reset state
            MBW_BTReset();
        }

        m_bBTOpenFlag = TRUE;
        return bReturn;
    }

    virtual BOOL  CPXAPdd16550BUART::Close()
    {
        BOOL bReturn =  CPXAPdd16550::Close();
        // device is closed, notify IPM that no restricts from FFUART side.
        RETAILMSG(1, (_T("[%s]: device is closed, notify IPM that no restricts from FFUART side.\r\n"), szName));

        /* Check if it need to turn off Bluetooth part on Combo card */
        if (m_bBluetoothUsageFlag)
        {
            RETAILMSG(1, (_T("[BTUART: Bluetooth] Start Disable BT.\r\n")));
            MBW_BTClkEnable(FALSE);
        }

        // Disable BTUART clock during driver is cloesed
        XllpClockEnable(m_pDCCLKReg, XLLP_CLK_BTUART, XLLP_FALSE);
        m_bBTOpenFlag = FALSE;

        return bReturn;

    }

        virtual void  SerialRegisterBackup() {

         CPXAPdd16550::SerialRegisterBackup();

         //NKDbgPrintfW(TEXT("MS2_Serial***: Disabling UART\r\n"));
         BYTE bIER = GetRegister()->Read_IER();
         GetRegister()->Write_IER(bIER & ~PXA_SERIAL_IER_UUE);

         //Disable BTUART clock
         XllpClockEnable(m_pDCCLKReg, XLLP_CLK_BTUART, XLLP_FALSE);

         /* Check if it need to turn off */
         if (m_bBluetoothUsageFlag)
         {
             RETAILMSG(1, (_T("[BTUART: Bluetooth] Start Disable BT.\r\n")));
             MBW_BTClkEnable(FALSE);
         }
     }


    virtual void    SerialRegisterRestore()
    {
        ConfigurePinout();

        //Enable BTUART clock
        XllpClockEnable(m_pDCCLKReg, XLLP_CLK_BTUART, XLLP_TRUE);

        CPXAPdd16550::SerialRegisterRestore();

        //NKDbgPrintfW(TEXT("MS2_Serial***: Enabling UART\r\n"));
        BYTE bIER = GetRegister()->Read_IER();
        GetRegister()->Write_IER(bIER | PXA_SERIAL_IER_UUE);

        if (m_DMAEnabled)
        {
            /* It need to re configure DMA receive channel register after wake up from sleep */
            CPXAPdd16550::DMAReInitRXChannel();
        }

        /* Check if it need to turn on Bluetooth part on Combo card */
        if (m_bBluetoothUsageFlag)
        {
            /* Only turn on combo card when BTUART is opened */
            if (m_bBTOpenFlag)
            {
                RETAILMSG(1, (_T("[BTUART: Bluetooth] Start Enable BT.\r\n")));

                /* Currrently, only support Rev 1.2 combo card */
                // Enable the BT 26MHz clock
                MBW_BTClkEnable(TRUE);
                // Make the BT core out of the reset state
                MBW_BTReset();
            }
        }
    }
private:
    ///--
    BOOL         m_bBluetoothUsageFlag;
    BOOL         m_bBTOpenFlag;

    //P_XLLP_GPIO_T m_pGPIORegs;


    void MBW_BTClkEnable(BOOL bPowerFlag)
    {
    }

    void MBW_BTReset(void)
    {
    }
    ///--

    BOOL ConfigurePinout() {
    XLLP_STATUS_T returnValue = XLLP_STATUS_FAILURE;

        //Configuring GPIO pins for BTUART
        //Initialize GPIO pins

        m_UART_Handle.UARTSelection = XLLP_BTUART;
        m_UART_Handle.pUARTRegs = (P_XLLP_UART_T) m_pBaseAddress;
        m_UART_Handle.pMfpRegBase = (XLLP_VUINT32_T *) m_pMFPReg ;
        m_UART_Handle.pGPIORegs = m_pGPIOReg;
        m_UART_Handle.pMfpRmDb = m_pMfpRMDb;
        m_UART_Handle.MfpRmDBID = XLLP_MFP_RM_ID_XLLP_BTUART;
        m_UART_Handle.Mask = XLLP_DLL_DLH_SET | XLLP_LCR_SET | XLLP_FCR_SET
            | XLLP_FCR_SET | XLLP_IER_SET | XLLP_ABR_SET
            | XLLP_MCR_SET | XLLP_ISR_SET;
        m_UART_Handle.DLL_Value = XLLP_DLL_115200BAUD;
        m_UART_Handle.DLH_Value = XLLP_DLH_115200BAUD;
        m_UART_Handle.LCR_Value=  (XLLP_LCR_EPS | XLLP_LCR_PEN ) ;
        m_UART_Handle.FCR_Value= 0;
        m_UART_Handle.IER_Value= 0;
        m_UART_Handle.ABR_Value= 0;
        m_UART_Handle.MCR_Value= 0;
        m_UART_Handle.ISR_Value= 0;
        returnValue = XllpUartInit(&m_UART_Handle);
        if (XLLP_STATUS_SUCCESS != returnValue)
        {
            NKDbgPrintfW(TEXT("\r\n[UART] Fail to XllpUartInit(BTUART).\r\n"));
            RETAILMSG(1,(TEXT("[UART] Fail to XllpUartInit(BTUART): Code#%x\r\n"), returnValue));
        }
        return TRUE;
    }
};
//---------------------------------SUART PORT -----------------------------------
class CPXAPdd16550SUART : public CPXAPdd16550
{
public:
    CPXAPdd16550SUART (LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj)
        : CPXAPdd16550 (lpActivePath,pMdd, pHwObj)
    {
        wcscpy(szName, L"STUART");
    };
    ~CPXAPdd16550SUART()
    {

    }
    virtual BOOL Init()
    {
        BSP_ARGS args;
        DWORD size;
        HKEY hKey=NULL;

       m_UART_Handle.UARTSelection = XLLP_STUART;

       /* For IRDA, it can not use DMA mode and hardware flow control */
       m_AutoFlowControlEnabled = 0;
       m_DMAEnabled = 0;

       if(KernelIoControl(IOCTL_GET_BSP_ARGS,NULL,0,&args,sizeof(args),&size))
       {
		if(args.STUARTUsage&BSP_ARGS_STUART_DEBUG_SERIAL)    
            {
                RETAILMSG(1, (_T("[STUART]EBOOT Config STUART for debug serial, it can not be used for other application!\r\n")));
                return FALSE;
            }
        }

        if (CPXAPdd16550::Init())
        {
            ConfigurePinout();
			RETAILMSG(1, (_T("[STUART]SUART Init Success\r\n")));
            return TRUE;
        }
        else
            return FALSE;
    }

    virtual BOOL  Open()
    {
        BOOL bReturn;
        RETAILMSG(UART_DEBUG, (TEXT("[STUART]Open\r\n")));

     /* TODO: Add IrDA support here - Littleton */

        XllpClockEnable(m_pDCCLKReg, XLLP_CLK_STUART, XLLP_TRUE);
		if(GPX_SetOutputLevel(XLLP_GPIO_GPS_LDO_EN, XLLP_HI) != XLLP_STATUS_SUCCESS)
        {
            RETAILMSG(1, (_T("Set XLLP_GPIO_GPS_POWERON HI FAIL\r\n")));
        }


        bReturn = CPXAPdd16550::Open();
        RETAILMSG(UART_DEBUG, (TEXT("[STUART]Open Completed\r\n")));
        return bReturn;
    }
 virtual BOOL  Close()
    {
        RETAILMSG(UART_DEBUG, (TEXT("[STUART]Close\r\n")));
        BOOL bReturn =  CPXAPdd16550::Close();
        if(GPX_SetOutputLevel(XLLP_GPIO_GPS_LDO_EN, XLLP_LO) != XLLP_STATUS_SUCCESS)
      {
            RETAILMSG(1, (_T("Set XLLP_GPIO_GPS_POWERON LO FAIL\r\n")));
      }

        //Disable STUART clock
        XllpClockEnable(m_pDCCLKReg, XLLP_CLK_STUART, XLLP_FALSE);
        return bReturn;
    }

    virtual void    SerialRegisterRestore()
    {
        ConfigurePinout();
        CPXAPdd16550::SerialRegisterRestore();

        if (m_DMAEnabled)
        {
            /* It need to re configure DMA receive channel register after wake up from sleep */
            CPXAPdd16550::DMAReInitRXChannel();
        }

    }

private:

    BOOL ConfigurePinout()
    {
        XLLP_STATUS_T returnValue = XLLP_STATUS_FAILURE;

        //Initialize GPIO pins
        m_UART_Handle.UARTSelection = XLLP_STUART;
        m_UART_Handle.pUARTRegs = (P_XLLP_UART_T) m_pBaseAddress;
        m_UART_Handle.pMfpRegBase = (XLLP_VUINT32_T *) m_pMFPReg ;
        m_UART_Handle.pGPIORegs = m_pGPIOReg;
        m_UART_Handle.pMfpRmDb = m_pMfpRMDb;
        m_UART_Handle.MfpRmDBID = XLLP_MFP_RM_ID_XLLP_STUART;
        m_UART_Handle.Mask = XLLP_DLL_DLH_SET | XLLP_LCR_SET | XLLP_FCR_SET
            | XLLP_FCR_SET | XLLP_IER_SET | XLLP_ABR_SET
            | XLLP_MCR_SET | XLLP_ISR_SET;
        m_UART_Handle.DLL_Value = XLLP_DLL_19200BAUD;
        m_UART_Handle.DLH_Value = XLLP_DLH_19200BAUD;
        m_UART_Handle.LCR_Value= 0;
        m_UART_Handle.FCR_Value= 0;
        m_UART_Handle.IER_Value= 0;
        m_UART_Handle.ABR_Value= 0;
        m_UART_Handle.MCR_Value= 0;
        m_UART_Handle.ISR_Value= 0;
        returnValue = XllpUartInit(&m_UART_Handle);
        if (XLLP_STATUS_SUCCESS != returnValue)
        {
            RETAILMSG(1,(TEXT("[UART] Fail to XllpUartInit(STUART):Code#%x.\r\n"), returnValue));
        }

        //Configure IrDA transceiver for SIR mode and full distance power.
        //m_IRDA_Handle.IrDAType = XLLP_SIR;
        m_IRDA_Handle.pUARTHandle = &m_UART_Handle;
        return TRUE;
    }

};

CSerialPDD * CreateSerialObject(LPTSTR lpActivePath, PVOID pMdd,PHWOBJ pHwObj, DWORD DeviceArrayIndex)
{
    CSerialPDD * pSerialPDD = NULL;

    switch (DeviceArrayIndex )
    {
        case 0:default:
            pSerialPDD = new CPdd16550(lpActivePath,pMdd, pHwObj);
            break;
        case 0x80:
            RETAILMSG(1, (_T("[FFUART] CreateSerialObject!\r\n")));
            pSerialPDD = new CPXAPdd16550FUART(lpActivePath,pMdd, pHwObj);
            break;
        case 0x81:
            RETAILMSG(1, (_T("[BTUART] CreateSerialObject!\r\n")));
            pSerialPDD = new CPXAPdd16550BUART(lpActivePath,pMdd, pHwObj);
            break;
        case 0x82:
            RETAILMSG(1, (_T("[STUART] CreateSerialObject!\r\n")));
            pSerialPDD = new CPXAPdd16550SUART(lpActivePath,pMdd, pHwObj);
            break;
    }

    if (pSerialPDD && pSerialPDD->Init()!= TRUE) {
        delete pSerialPDD;
        pSerialPDD = NULL;
    }

    return pSerialPDD;
}

void DeleteSerialObject(CSerialPDD * pSerialPDD)
{
    if (pSerialPDD)
        delete pSerialPDD;
}

